#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Chrome 性能监控脚本 - Windows 优化版本
专门针对 Windows 平台优化，与任务管理器保持一致的内存统计
"""

import os
import csv
import time
import psutil
import signal
import logging
import platform
from datetime import datetime
from typing import List, Dict, Optional
from pathlib import Path
from collections import deque
from influxdb_client import Point
from Common.handle_influxdb import InfluxDBHandler

do_influxdb = InfluxDBHandler()
# Windows 特殊处理
if platform.system() == "Windows":
    try:
        import win32api
        import win32process
        import win32con
        import win32pdh  # PDH: 性能计数器，CPU 与任务管理器一致
        import wmi
        HAS_WIN32 = True
    except ImportError:
        HAS_WIN32 = False
        print("警告: 未安装 pywin32 和 WMI 库，将使用标准 psutil（可能与任务管理器有差异）")
        print("安装命令: pip install pywin32 WMI")
else:
    HAS_WIN32 = False


# 内置配置（替代命令行参数）
INTERNAL_CONFIG = {
    "interval": 1.0,            # 监控间隔（秒）
    "output_dir": ".",         # 输出目录
    "playwright_only": False,   # 仅监控 Playwright 启动的进程
    "compare": False,           # 是否启动对比任务管理器模式
    "log_level": "INFO",       # 日志级别: DEBUG/INFO/WARNING/ERROR
}


class WindowsCompatibleChromeMonitor:
    """Windows 兼容的 Chrome 浏览器性能监控器"""
    
    def __init__(self, output_dir: str = ".", log_level: str = "INFO"):
        """
        初始化监控器
        
        Args:
            output_dir: 输出目录
            log_level: 日志级别
        """
        self.output_dir = Path(output_dir)
        self.output_dir.mkdir(exist_ok=True)
        
        # 检测操作系统
        self.is_windows = platform.system() == "Windows"
        self.has_win32 = HAS_WIN32 and self.is_windows
        
        # 初始化 WMI（Windows 专用）
        if self.has_win32:
            try:
                self.wmi_conn = wmi.WMI()
                self.logger_info = "使用 WMI 接口，与任务管理器保持一致"
            except Exception as e:
                self.has_win32 = False
                self.logger_info = f"WMI 初始化失败，使用标准接口: {e}"
        else:
            self.logger_info = "非 Windows 系统或缺少依赖，使用标准接口"
        
        # 设置日志
        logging.basicConfig(
            level=getattr(logging, log_level.upper()),
            format='%(asctime)s - %(levelname)s - %(message)s'
        )
        self.logger = logging.getLogger(__name__)
        
        # CPU 信息
        self.logical_cpu_count = psutil.cpu_count(logical=True) or 1

        # WMI 批量缓存（每次采样循环刷新一次，避免逐 PID 频繁查询）
        self._wmi_proc_cache: Dict[int, Dict] = {}
        self._wmi_perf_formatted_cache: Dict[int, Dict] = {}
        self._wmi_perf_raw_cache: Dict[int, Dict] = {}

        # PDH（CPU 更贴近任务管理器）
        self.has_pdh = False
        self._pdh_query = None
        self._pdh_cpu_counters_by_instance: Dict[str, int] = {}
        self._pdh_pid_counters_by_instance: Dict[str, int] = {}
        self._pdh_instances: set = set()
        self._pdh_instance_to_pid: Dict[str, int] = {}
        self._pdh_pid_to_instance: Dict[int, str] = {}

        # 平滑：与任务管理器类似做轻度平滑（窗口 3）
        self._per_pid_cpu_history: Dict[int, deque] = {}
        self._total_cpu_history: deque = deque(maxlen=3)
        if self.is_windows:
            try:
                # 如果 win32pdh 可用，则启用 PDH
                import win32pdh  # noqa: F401 仅用于探测可用性
                self.has_pdh = True
            except Exception:
                self.has_pdh = False

        # 生成输出文件名
        timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
        self.csv_file = self.output_dir / f"chrome_performance_win_{timestamp}.csv"
        
        # 监控状态
        self.is_monitoring = False
        self.monitoring_interval = 1.0
        
        # 初始化 CSV 文件
        self._init_csv_file()
        
        self.logger.info(f"Windows 优化性能监控器已初始化")
        self.logger.info(f"内存统计方式: {self.logger_info}")
        self.logger.info(f"输出文件: {self.csv_file}")
    
    def _init_csv_file(self):
        """初始化 CSV 文件并写入表头"""
        headers = [
            "timestamp",               # 时间戳
            "process_type",           # 进程类型
            "pid",                    # 进程ID
            "ppid",                   # 父进程ID
            "name",                   # 进程名称
            "cmdline",                # 命令行参数
            "cpu_percent",            # CPU 使用率 (%)
            "cpu_source",             # CPU 数据来源
            "memory_working_set_mb",  # 工作集内存 (MB) - 对应任务管理器的"内存"
            "memory_private_mb",      # 私有内存 (MB) - 对应任务管理器的"私有工作集"
            "memory_shared_mb",       # 共享内存 (MB)
            "memory_virtual_mb",      # 虚拟内存 (MB)
            "memory_percent",         # 内存使用率 (%)
            "num_threads",            # 线程数
            "num_handles",            # 句柄数（Windows 特有）
            "status",                 # 进程状态
            "create_time",            # 创建时间
            "is_playwright_launched", # 是否为 Playwright 启动
            "memory_source"           # 内存数据来源
        ]
        
        with open(self.csv_file, 'w', newline='', encoding='utf-8') as f:
            writer = csv.writer(f)
            writer.writerow(headers)
    
    def _refresh_wmi_caches(self, pid_list: Optional[List[int]] = None):
        """刷新一次 WMI 批量缓存（仅 Windows + 具备 pywin32/wmi 时生效）"""
        if not self.has_win32:
            # 非 Windows 或缺少依赖，不刷新缓存
            self._wmi_proc_cache = {}
            self._wmi_perf_formatted_cache = {}
            self._wmi_perf_raw_cache = {}
            return
        try:
            # Win32_Process: WorkingSetSize, VirtualSize, HandleCount
            if pid_list:
                pid_list_str = ','.join(str(p) for p in pid_list)
                query = (
                    "SELECT ProcessId, WorkingSetSize, VirtualSize, HandleCount "
                    f"FROM Win32_Process WHERE ProcessId IN ({pid_list_str})"
                )
            else:
                query = (
                    "SELECT ProcessId, WorkingSetSize, VirtualSize, HandleCount FROM Win32_Process"
                )
            proc_rows = self.wmi_conn.query(query)
            self._wmi_proc_cache = {}
            for row in proc_rows:
                try:
                    pid = int(row.ProcessId)
                except Exception:
                    continue
                self._wmi_proc_cache[pid] = {
                    "WorkingSetSize": int(row.WorkingSetSize or 0),
                    "VirtualSize": int(row.VirtualSize or 0),
                    "HandleCount": int(row.HandleCount or 0),
                }
            # 为避免卡顿，不进行全表 PerfFormatted/PerfRaw 扫描；按需逐 PID 查询
            self._wmi_perf_formatted_cache = {}
            self._wmi_perf_raw_cache = {}
        except Exception as e:
            # 任一失败则清空，避免使用过期缓存
            self.logger.debug(f"刷新 WMI 缓存失败: {e}")
            self._wmi_proc_cache = {}
            self._wmi_perf_formatted_cache = {}
            self._wmi_perf_raw_cache = {}

    def _wmi_query_perf_for_pid(self, pid: int) -> Dict[str, int]:
        """按需查询某个 PID 的 WorkingSetPrivate / PrivateBytes（避免全表扫描）"""
        result = {"WorkingSetPrivate": 0, "PrivateBytes": 0}
        if not self.has_win32:
            return result
        try:
            rows = self.wmi_conn.query(
                f"SELECT WorkingSetPrivate, PrivateBytes FROM Win32_PerfRawData_PerfProc_Process WHERE IDProcess = {pid}"
            )
            if rows:
                row = rows[0]
                result["WorkingSetPrivate"] = int(getattr(row, 'WorkingSetPrivate', 0) or 0)
                result["PrivateBytes"] = int(getattr(row, 'PrivateBytes', 0) or 0)
        except Exception as e:
            self.logger.debug(f"WMI 单 PID 性能查询失败 PID {pid}: {e}")
        return result

    # ---------------- PDH 相关：总 CPU 更贴近任务管理器 ----------------
    def _pdh_rebuild_counters_if_needed(self):
        if not self.has_pdh:
            return
        try:
            # 枚举进程实例
            instances, _ = win32pdh.EnumObjectItems(None, None, 'Process', win32pdh.PERF_DETAIL_WIZARD)
            chrome_instances = {name for name in instances if name.lower().startswith('chrome')}
            if chrome_instances != self._pdh_instances:
                # 重建查询
                if self._pdh_query is not None:
                    try:
                        win32pdh.CloseQuery(self._pdh_query)
                    except Exception:
                        pass
                self._pdh_query = win32pdh.OpenQuery()
                self._pdh_cpu_counters_by_instance = {}
                self._pdh_pid_counters_by_instance = {}
                for inst in chrome_instances:
                    cpu_path = win32pdh.MakeCounterPath((None, 'Process', inst, None, -1, '% Processor Time'))
                    pid_path = win32pdh.MakeCounterPath((None, 'Process', inst, None, -1, 'ID Process'))
                    cpu_counter = win32pdh.AddCounter(self._pdh_query, cpu_path)
                    pid_counter = win32pdh.AddCounter(self._pdh_query, pid_path)
                    self._pdh_cpu_counters_by_instance[inst] = cpu_counter
                    self._pdh_pid_counters_by_instance[inst] = pid_counter
                # 记录实例集
                self._pdh_instances = chrome_instances
                # 预采样一次，PDH 需要两次采样计算百分比
                win32pdh.CollectQueryData(self._pdh_query)
                # 建立实例与 PID 映射
                self._pdh_instance_to_pid = {}
                self._pdh_pid_to_instance = {}
                for inst, pid_counter in self._pdh_pid_counters_by_instance.items():
                    try:
                        _, pid_val = win32pdh.GetFormattedCounterValue(pid_counter, win32pdh.PDH_FMT_LONG)
                        pid_int = int(pid_val)
                        if pid_int > 0:
                            self._pdh_instance_to_pid[inst] = pid_int
                            self._pdh_pid_to_instance[pid_int] = inst
                    except Exception:
                        continue
        except Exception as e:
            self.logger.debug(f"PDH 计数器重建失败: {e}")

    def _pdh_collect_total_chrome_cpu(self) -> float:
        if not self.has_pdh or self._pdh_query is None:
            return 0.0
        try:
            win32pdh.CollectQueryData(self._pdh_query)
            total = 0.0
            for inst, counter in self._pdh_cpu_counters_by_instance.items():
                try:
                    t, val = win32pdh.GetFormattedCounterValue(counter, win32pdh.PDH_FMT_DOUBLE)
                    total += float(val)
                except Exception:
                    continue
            # 归一化到 0-100（任务管理器口径）
            total /= float(self.logical_cpu_count)
            if total < 0:
                total = 0.0
            return total
        except Exception as e:
            self.logger.debug(f"PDH 采样失败: {e}")
            return 0.0

    def _pdh_collect_per_process_cpu(self) -> Dict[int, float]:
        """返回 {pid: cpu_percent}，基于 PDH，更接近任务管理器“详细信息”页。"""
        result: Dict[int, float] = {}
        if not self.has_pdh or self._pdh_query is None:
            return result
        try:
            win32pdh.CollectQueryData(self._pdh_query)
            for inst, counter in self._pdh_cpu_counters_by_instance.items():
                try:
                    _, val = win32pdh.GetFormattedCounterValue(counter, win32pdh.PDH_FMT_DOUBLE)
                    pid = self._pdh_instance_to_pid.get(inst)
                    if pid:
                        cpu_percent = float(val) / float(self.logical_cpu_count)
                        if cpu_percent < 0:
                            cpu_percent = 0.0
                        result[pid] = cpu_percent
                except Exception:
                    continue
        except Exception as e:
            self.logger.debug(f"PDH per-process 采样失败: {e}")
        return result

    def _get_windows_memory_info(self, pid: int) -> Dict:
        """
        获取 Windows 进程的详细内存信息（与任务管理器一致）
        
        Args:
            pid: 进程ID
            
        Returns:
            Dict: 内存信息字典
        """
        memory_info = {
            "working_set_mb": 0,
            "private_mb": 0,
            "shared_mb": 0,
            "virtual_mb": 0,
            "source": "psutil"
        }
        
        if not self.has_win32:
            # 使用标准 psutil
            try:
                process = psutil.Process(pid)
                mem_info = process.memory_info()
                memory_info.update({
                    "working_set_mb": round(mem_info.rss / 1024 / 1024, 2),
                    "virtual_mb": round(mem_info.vms / 1024 / 1024, 2),
                    "source": "psutil"
                })
            except:
                pass
            return memory_info
        
        # 使用 WMI 批量缓存获取更准确的内存信息
        try:
            proc_row = self._wmi_proc_cache.get(pid)
            if proc_row:
                working_set = int(proc_row.get("WorkingSetSize", 0))
                virtual_size = int(proc_row.get("VirtualSize", 0))
                memory_info.update({
                    "working_set_mb": round(working_set / 1024 / 1024, 2),
                    "virtual_mb": round(virtual_size / 1024 / 1024, 2),
                    "source": "wmi"
                })

                # 优先尝试按需查询的 WorkingSetPrivate，不足再回退 PrivateBytes
                perf_vals = self._wmi_perf_raw_cache.get(pid)
                if not perf_vals:
                    perf_vals = self._wmi_query_perf_for_pid(pid)
                    self._wmi_perf_raw_cache[pid] = perf_vals
                if int(perf_vals.get("WorkingSetPrivate", 0)) > 0:
                    private_bytes = int(perf_vals.get("WorkingSetPrivate", 0))
                else:
                    private_bytes = int(perf_vals.get("PrivateBytes", 0))

                memory_info["private_mb"] = round(private_bytes / 1024 / 1024, 2)
                if working_set > private_bytes:
                    memory_info["shared_mb"] = round((working_set - private_bytes) / 1024 / 1024, 2)
                return memory_info
        except Exception as e:
            self.logger.debug(f"读取 WMI 缓存失败 PID {pid}: {e}")

        # 回退到 psutil
        try:
            process = psutil.Process(pid)
            mem_info = process.memory_info()
            memory_info.update({
                "working_set_mb": round(mem_info.rss / 1024 / 1024, 2),
                "virtual_mb": round(mem_info.vms / 1024 / 1024, 2),
                "source": "psutil_fallback"
            })
        except Exception:
            pass
        
        return memory_info
    
    def _get_windows_handle_count(self, pid: int) -> int:
        """
        获取 Windows 进程的句柄数
        
        Args:
            pid: 进程ID
            
        Returns:
            int: 句柄数
        """
        if not self.has_win32:
            return 0
        
        try:
            row = self._wmi_proc_cache.get(pid)
            if row and "HandleCount" in row:
                return int(row.get("HandleCount") or 0)
        except Exception:
            pass
        
        return 0
    
    def _is_playwright_chrome(self, process: psutil.Process) -> bool:
        """
        判断是否为 Playwright 启动的 Chrome 进程
        
        Args:
            process: 进程对象
            
        Returns:
            bool: 是否为 Playwright 启动的 Chrome
        """
        try:
            cmdline = process.cmdline()
            if not cmdline:
                return False
            
            cmdline_str = ' '.join(cmdline)
            
            # Playwright 启动的 Chrome 特征标识
            playwright_indicators = [
                '--remote-debugging-port=',
                '--user-data-dir=',
                '--no-first-run',
                '--disable-dev-shm-usage',
                '--disable-blink-features=AutomationControlled',
                '--disable-background-timer-throttling',
                '--disable-backgrounding-occluded-windows',
                '--disable-renderer-backgrounding',
            ]
            
            # 检查是否包含 Playwright 特征
            playwright_count = sum(1 for indicator in playwright_indicators 
                                 if indicator in cmdline_str)
            
            return playwright_count >= 2
            
        except (psutil.NoSuchProcess, psutil.AccessDenied, IndexError):
            return False
    
    def _get_chrome_process_type(self, process: psutil.Process) -> str:
        """
        获取 Chrome 进程类型
        
        Args:
            process: 进程对象
            
        Returns:
            str: 进程类型
        """
        try:
            cmdline = process.cmdline()
            if not cmdline:
                return "unknown"
            
            cmdline_str = ' '.join(cmdline)
            
            # 判断进程类型
            if '--type=renderer' in cmdline_str:
                return "renderer"
            elif '--type=gpu-process' in cmdline_str:
                return "gpu"
            elif '--type=utility' in cmdline_str:
                return "utility"
            elif '--type=broker' in cmdline_str:
                return "broker"
            elif '--type=crashpad-handler' in cmdline_str:
                return "crashpad"
            elif '--type=zygote' in cmdline_str:
                return "zygote"
            elif any(name in process.name().lower() for name in ['chrome', 'chromium']) and '--type=' not in cmdline_str:
                return "main"
            else:
                return "unknown"
                
        except (psutil.NoSuchProcess, psutil.AccessDenied, IndexError):
            return "unknown"
    
    def _collect_process_info(self, process: psutil.Process) -> Optional[Dict]:
        """
        收集单个进程的性能信息
        
        Args:
            process: 进程对象
            
        Returns:
            Dict: 进程信息字典，如果收集失败返回 None
        """
        try:
            with process.oneshot():
                # 获取基本信息
                pid = process.pid
                # CPU 使用率：优先 PDH（任务管理器同源），其次 WMI PerfFormatted
                cpu_source = "psutil"
                cpu_percent = 0.0
                if self.has_pdh and self._pdh_query is not None:
                    # 优先使用 PDH 的 per-process 结果（与任务管理器一致）
                    try:
                        per_proc_cpu = getattr(self, '_last_pdh_per_proc', None)
                        if per_proc_cpu is None:
                            per_proc_cpu = self._pdh_collect_per_process_cpu()
                        cpu_val = per_proc_cpu.get(pid)
                        if cpu_val is not None:
                            cpu_percent = float(cpu_val)
                            cpu_source = "pdh"
                        else:
                            raise RuntimeError("pdh_missing_pid")
                    except Exception:
                        pass
                if cpu_source == "psutil" and self.has_win32:
                    try:
                        perf_f = self._wmi_perf_formatted_cache.get(pid)
                        if perf_f is not None:
                            cpu_percent = float(perf_f.get("PercentProcessorTime", 0.0))
                            cpu_source = "wmi"
                        else:
                            # 回退 psutil（注意首次调用可能为 0.0）
                            raw_cpu = process.cpu_percent(interval=None)
                            # 归一化到任务管理器口径（百分比相对于整机 100%）
                            cpu_percent = raw_cpu / float(self.logical_cpu_count)
                            cpu_source = "psutil_fallback"
                    except Exception:
                        raw_cpu = process.cpu_percent(interval=None)
                        cpu_percent = raw_cpu / float(self.logical_cpu_count)
                        cpu_source = "psutil_fallback"
                else:
                    # 非 Windows 或无 WMI，仅使用 psutil
                    cpu_percent = process.cpu_percent(interval=None)
                    cpu_source = "psutil"
                
                # 获取 Windows 优化的内存信息
                memory_info = self._get_windows_memory_info(pid)
                
                # 获取句柄数（Windows 特有）
                handle_count = self._get_windows_handle_count(pid) if self.is_windows else 0
                
                info = {
                    "timestamp": datetime.now().isoformat(),
                    "process_type": self._get_chrome_process_type(process),
                    "pid": pid,
                    "ppid": process.ppid(),
                    "name": process.name(),
                    "cmdline": ' '.join(process.cmdline()[:3]) + "..." if len(process.cmdline()) > 3 else ' '.join(process.cmdline()),
                    "cpu_percent": round(float(cpu_percent), 2),
                    "cpu_source": cpu_source,
                    "memory_working_set_mb": memory_info["working_set_mb"],
                    "memory_private_mb": memory_info["private_mb"],
                    "memory_shared_mb": memory_info["shared_mb"],
                    "memory_virtual_mb": memory_info["virtual_mb"],
                    "memory_percent": round(process.memory_percent(), 2),
                    "num_threads": process.num_threads(),
                    "num_handles": handle_count,
                    "status": process.status(),
                    "create_time": datetime.fromtimestamp(process.create_time()).isoformat(),
                    "is_playwright_launched": self._is_playwright_chrome(process),
                    "memory_source": memory_info["source"]
                }
                
                return info
                
        except (psutil.NoSuchProcess, psutil.AccessDenied, psutil.ZombieProcess) as e:
            self.logger.debug(f"无法获取进程 {process.pid} 信息: {e}")
            return None
    
    def _find_chrome_processes(self) -> List[psutil.Process]:
        """
        查找所有 Chrome 相关进程
        
        Returns:
            List[psutil.Process]: Chrome 进程列表
        """
        chrome_processes = []
        
        for process in psutil.process_iter(['pid', 'name', 'cmdline']):
            try:
                process_name = process.info['name'].lower()
                
                # 只匹配 Google Chrome 浏览器进程名
                if any(chrome_name in process_name for chrome_name in [
                    'chrome', 'chromium', 'google chrome'
                ]):
                    chrome_processes.append(process)
                    
            except (psutil.NoSuchProcess, psutil.AccessDenied):
                continue
        
        return chrome_processes
    
    def _write_to_csv(self, process_info: Dict):
        """
        将进程信息写入 CSV 文件
        
        Args:
            process_info: 进程信息字典
        """
        try:
            with open(self.csv_file, 'a', newline='', encoding='utf-8') as f:
                writer = csv.writer(f)
                row = [
                    process_info["timestamp"],
                    process_info["process_type"],
                    process_info["pid"],
                    process_info["ppid"],
                    process_info["name"],
                    process_info["cmdline"],
                    process_info["cpu_percent"],
                    process_info.get("cpu_source", "unknown"),
                    process_info["memory_working_set_mb"],
                    process_info["memory_private_mb"],
                    process_info["memory_shared_mb"],
                    process_info["memory_virtual_mb"],
                    process_info["memory_percent"],
                    process_info["num_threads"],
                    process_info["num_handles"],
                    process_info["status"],
                    process_info["create_time"],
                    process_info["is_playwright_launched"],
                    process_info["memory_source"]
                ]
                writer.writerow(row)
        except Exception as e:
            self.logger.error(f"写入 CSV 文件失败: {e}")
    
    def start_monitoring(self, interval: float = 1.0, filter_playwright_only: bool = False):
        """
        开始性能监控
        
        Args:
            interval: 监控间隔（秒）
            filter_playwright_only: 是否只监控 Playwright 启动的进程
        """
        self.monitoring_interval = interval
        self.is_monitoring = True
        
        self.logger.info("开始监控 Chrome 浏览器性能...")
        self.logger.info(f"操作系统: {platform.system()}")
        self.logger.info(f"内存统计方式: {self.logger_info}")
        self.logger.info(f"监控间隔: {interval} 秒")
        self.logger.info(f"只监控 Playwright 进程: {filter_playwright_only}")
        self.logger.info(f"输出文件: {self.csv_file}")
        self.logger.info("按 Ctrl+C 停止监控")
        
        try:
            while self.is_monitoring:
                loop_start = time.time()
                # 每次采样循环刷新一次 WMI 缓存，确保与任务管理器同步
                if self.has_win32:
                    self._refresh_wmi_caches()
                # 根据需要重建 PDH 计数器（实例变化时）
                if self.has_pdh:
                    self._pdh_rebuild_counters_if_needed()
                    # 提前采一次 per-process CPU，供后续逐进程读取使用（避免每进程重复取数导致时间抖动）
                    self._last_pdh_per_proc = self._pdh_collect_per_process_cpu()
                chrome_processes = self._find_chrome_processes()
                
                if not chrome_processes:
                    self.logger.debug("未找到 Chrome 进程")
                else:
                    playwright_count = 0
                    manual_count = 0
                    total_working_set = 0
                    total_private_ws = 0
                    total_cpu_percent = 0.0
                    total_cpu_percent_pdh = 0.0
                    
                    for process in chrome_processes:
                        process_info = self._collect_process_info(process)
                        
                        if process_info:
                            is_playwright = process_info["is_playwright_launched"]
                            
                            if filter_playwright_only and not is_playwright:
                                continue
                            
                            # self._write_to_csv(process_info)#不写入CSV
                            total_working_set += process_info["memory_working_set_mb"]
                            total_private_ws += process_info["memory_private_mb"]
                            # 平滑 per-process CPU
                            pid_cpu = float(process_info.get("cpu_percent", 0.0))
                            hist = self._per_pid_cpu_history.get(process_info["pid"]) or deque(maxlen=3)
                            hist.append(pid_cpu)
                            self._per_pid_cpu_history[process_info["pid"]] = hist
                            smoothed_pid_cpu = sum(hist) / len(hist)
                            total_cpu_percent += smoothed_pid_cpu
                            
                            if is_playwright:
                                playwright_count += 1
                            else:
                                manual_count += 1
                    # 以 PDH 口径计算总 CPU，更贴近任务管理器应用聚合视图
                    if self.has_pdh:
                        total_cpu_percent_pdh = self._pdh_collect_total_chrome_cpu()
                        # 平滑总 CPU
                        self._total_cpu_history.append(total_cpu_percent_pdh)
                        total_cpu_percent_pdh = sum(self._total_cpu_history) / len(self._total_cpu_history)
                    
                    if not filter_playwright_only:
                        self.logger.info(f"监控到 {len(chrome_processes)} 个 Chrome 进程 "
                                       f"(Playwright: {playwright_count}, 手动启动: {manual_count}) "
                                       f"总CPU: {(total_cpu_percent_pdh or total_cpu_percent):.1f}%, 总工作集: {total_working_set:.1f}MB, 总私有工作集: {total_private_ws:.1f}MB")
                    else:
                        self.logger.info(f"监控到 {playwright_count} 个 Playwright Chrome 进程 "
                                       f"总CPU: {(total_cpu_percent_pdh or total_cpu_percent):.1f}%, 总工作集: {total_working_set:.1f}MB, 总私有工作集: {total_private_ws:.1f}MB")
                    measurements = [
                        Point("device_001").tag("pid", "main").field("cpu", float(total_cpu_percent_pdh or total_cpu_percent)),
                        Point("device_001").tag("pid", "main").field("mem", float(total_private_ws))]
                    do_influxdb.write_mul_data(measurements=measurements)
                # 保持稳定采样间隔
                elapsed = time.time() - loop_start
                remain = interval - elapsed
                if remain > 0:
                    time.sleep(remain)
                
        except KeyboardInterrupt:
            self.logger.info("收到停止信号，正在停止监控...")
        except Exception as e:
            self.logger.error(f"监控过程中发生错误: {e}")
        finally:
            self.stop_monitoring()
    
    def stop_monitoring(self):
        """停止监控"""
        self.is_monitoring = False
        self.logger.info("性能监控已停止")
        self.logger.info(f"性能数据已保存到: {self.csv_file}")
    
    def compare_with_task_manager(self):
        """
        对比任务管理器数据（仅 Windows）
        """
        if not self.is_windows:
            print("此功能仅适用于 Windows 系统")
            return
        
        print("🔍 对比任务管理器数据...")
        print("请同时打开任务管理器进行对比")
        print("-" * 80)
        # 刷新一次 WMI 缓存，尽量与任务管理器当前时刻同步
        if self.has_win32:
            self._refresh_wmi_caches()
        
        chrome_processes = self._find_chrome_processes()
        
        if not chrome_processes:
            print("未找到 Chrome 进程")
            return
        
        print(f"{'进程类型':<12} {'PID':<8} {'CPU(%)':<8} {'工作集(MB)':<12} {'私有(MB)':<12} {'虚拟(MB)':<12} {'线程':<6} {'句柄':<8} {'CPU源':<8} {'内存源':<10}")
        print("-" * 80)
        
        total_working_set = 0
        total_cpu_percent = 0.0
        for process in chrome_processes:
            info = self._collect_process_info(process)
            if info:
                print(f"{info['process_type']:<12} "
                      f"{info['pid']:<8} "
                      f"{info['cpu_percent']:<8.1f} "
                      f"{info['memory_working_set_mb']:<12.1f} "
                      f"{info['memory_private_mb']:<12.1f} "
                      f"{info['memory_virtual_mb']:<12.1f} "
                      f"{info['num_threads']:<6} "
                      f"{info['num_handles']:<8} "
                      f"{info.get('cpu_source',''):<8} "
                      f"{info['memory_source']:<10}")
                total_working_set += info['memory_working_set_mb']
                total_cpu_percent += float(info.get('cpu_percent', 0.0))
        
        print("-" * 80)
        print(f"总CPU: {total_cpu_percent:.1f}%, 总工作集内存: {total_working_set:.1f} MB")
        print("\n💡 提示:")
        print("- 工作集内存应该与任务管理器的'内存'列相匹配")
        print("- 私有内存对应任务管理器的'私有工作集'")
        print("- 如果使用 WMI 数据源，精度会更高")


def signal_handler(signum, frame):
    """信号处理器"""
    print("\n收到停止信号，正在停止监控...")
    if 'monitor' in globals():
        monitor.stop_monitoring()
    exit(0)


def main():
    """主函数"""
    # 使用内部配置
    cfg = INTERNAL_CONFIG

    # 创建监控器
    global monitor
    monitor = WindowsCompatibleChromeMonitor(
        output_dir=cfg.get("output_dir", "."),
        log_level=cfg.get("log_level", "INFO")
    )
    
    # 注册信号处理器
    signal.signal(signal.SIGINT, signal_handler)
    if hasattr(signal, 'SIGTERM'):
        signal.signal(signal.SIGTERM, signal_handler)
    
    try:
        if cfg.get("compare", False):
            # 对比模式
            monitor.compare_with_task_manager()
        else:
            # 监控模式
            monitor.start_monitoring(
                interval=float(cfg.get("interval", 1.0)),
                filter_playwright_only=bool(cfg.get("playwright_only", False))
            )
            
    except Exception as e:
        print(f"监控过程中发生错误: {e}")
        return 1
    
    return 0


if __name__ == "__main__":
    exit(main())
